package Control;

import java.util.*;
import Model.*;
import View.SourceCodeWindow;

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Alan Carlos
 * @author João Vilas Boas
 */
/*
Classe contendo todos os autômatos para a indentificação de cada Token

*/

public class Automato {

    private Collection letter;    //opção de mudança posterior, sem ter que alterar o código todo
    private Collection digit;
    private Collection simbol;
    private String letraUP = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private String letraLW = letraUP.toLowerCase();
    private Collection identifier;
    private Collection operator;
    private Collection delimeters;
    private Collection reservedWord;
    private Collection notAlphabet;
    private TableLexema tabelaSimbolo;
    private SourceCodeWindow mainView;
    
    /*UTILIZAR CODIGOS DE TIPO PARA LEXEMAS:
     tipo = 1 : IDENTIFICADOR
     tipo = 2 : PALAVRA RESERVADA 
     tipo = 3 : SIMBOLO
     tipo = 4 : STRING
     tipo = 5 : CARACTERE
     tipo = 6 : OPERADOR
     tipo = 7 : DELIMITADOR
     tipo = 8 : NUMERO
     Armazenando um inteiro para identificar o tipo do lexema ao inves de 
    utilizar um String, conseguimos economizar memória
     */    
    
    public Automato(SourceCodeWindow view) {
        this.mainView = view;
        this.identifier = new ArrayList();
        this.letter = new ArrayList();
        this.operator = new ArrayList();
        this.delimeters = new ArrayList();
        this.reservedWord = new ArrayList();
        this.tabelaSimbolo = TableLexema.getInstance();
        for (int i = 0; i < letraUP.length(); i++) { //add letras
            letter.add(letraUP.charAt(i));
            letter.add(letraLW.charAt(i));
            identifier.add(letraUP.charAt(i));
            identifier.add(letraLW.charAt(i));
        }
        this.simbol = new ArrayList();
        for (int i = 32; i <= 126; i++) {   //add símbolos
            if (i != 34 && i != 39) {
                simbol.add((char) i);
            }
            if (i == 95) {
                identifier.add((char) i);
            }

        }
        
        
        this.notAlphabet = new ArrayList();
        //gera Array contendo os simbolos da tabela ASCII que não pertencem ao alfabeto
        for (int i = 0; i <= 31; i++)
        {   
            notAlphabet.add((char) i);

        }
        notAlphabet.add((char) 127);
        

        this.digit = new ArrayList();
        for (int i = 48; i <= 57; i++) {  //add digitos
            digit.add((char) i);
            identifier.add((char) i);
        }
        //adiciona todos os operadores do no nosso alfabeto
        operator.add('+');
        operator.add('-');
        operator.add('*');
        operator.add('/');
        operator.add('=');
        operator.add('>');
        operator.add('<');
        operator.add('&');
        operator.add('.');
        operator.add('!');
        operator.add('|');
        //adiciona todos os delimitadores do no nosso alfabeto
        delimeters.add(';');
        delimeters.add(',');
        delimeters.add('(');
        delimeters.add(')');
        delimeters.add('{');
        delimeters.add('}');
        delimeters.add('[');
        delimeters.add(']');
        //adiciona todas as palavraas chaves do no nosso alfabeto
        reservedWord.add("variables");
        reservedWord.add("methods");
        reservedWord.add("constants");
        reservedWord.add("class");
        reservedWord.add("return");
        reservedWord.add("empty");
        reservedWord.add("main");
        reservedWord.add("if");
        reservedWord.add("then");
        reservedWord.add("else");
        reservedWord.add("while");
        reservedWord.add("for");
        reservedWord.add("read");
        reservedWord.add("write");
        reservedWord.add("integer");
        reservedWord.add("float");
        reservedWord.add("boolean");
        reservedWord.add("string");
        reservedWord.add("true");
        reservedWord.add("false");
        reservedWord.add("extends");

    }

    public int checkIdentifiers(String texto, int index) {
        char currentChar;
        ArrayList<Token> array = new ArrayList<Token>();
        boolean flagError = false;
        String lexemaI = new String();
        String stringchar;
        int i;
        currentChar = texto.charAt(index);
        stringchar = String.valueOf(currentChar);
        lexemaI = lexemaI.concat(stringchar);

        for (i = index + 1; i < texto.length(); i++) {
            currentChar = texto.charAt(i);
            if (identifier.contains(currentChar)) {
                stringchar = String.valueOf(currentChar);
                lexemaI = lexemaI.concat(stringchar);
            } else if (operator.contains(currentChar) || delimeters.contains(currentChar) || currentChar == (char) 32 || elementText(currentChar) || currentChar == '\u001a') {
                if (reservedWord.contains(lexemaI)) {
                    System.out.println("Palavra reservada -->" + lexemaI);
                    TableLexema.getInstance().addToList(lexemaI, index, 2); //i-1
                    
                    //teste para ver se ta armazendo corretamente no arraylist
                    //TESTE OK
                    //array = TableLexema.getInstance().getArray();
                } else if (flagError) {
                    System.out.println("Erro na formação do identificador -->" + lexemaI);
                    TableLexema.getInstance().addErrorToList(lexemaI, index, "Erro na formação do identificador");
                } else {
                    System.out.println("Lexema Identificador -->" + lexemaI);
                    TableLexema.getInstance().addToList(lexemaI, index, 1);

                }

                flagError = false;
                lexemaI = "";
                break;
            } else { //tratamento caso identificador mal formado
                flagError = true;
                stringchar = String.valueOf(currentChar);
                lexemaI = lexemaI.concat(stringchar);

            }

        }

        return i - 1;

    }

    public int checkString(String texto, int index) {
        int  notSimbol=0;
        // boolean flagString = true;
        String lexemaStr = new String();
        char currentChar = ' ';
        int i;
        for (i = index + 1; i < texto.length(); i++) {
            currentChar = texto.charAt(i);
            tabelaSimbolo.incrementColumn();
            if (letter.contains(currentChar) || simbol.contains(currentChar) || digit.contains(currentChar) || elementText(currentChar)) {
                lexemaStr = lexemaStr.concat(String.valueOf(currentChar));
            } else if (currentChar == (char) 34) {//fechou aspas.Acabou de fazer string.
                // flagString = false;
                System.out.println("String -->" + lexemaStr);
                TableLexema.getInstance().addToList(lexemaStr, index, 4);

                break;
            } else {
                while (currentChar != (char) 34 && currentChar != Triagem.EOF) {
                    lexemaStr = lexemaStr.concat(String.valueOf(currentChar));
                    i++;
                    currentChar = texto.charAt(i);
                    tabelaSimbolo.incrementColumn();
               /*     if(!simbol.contains(currentChar) && currentChar!=(char) 39 && currentChar!=(char)34 && currentChar!=Triagem.EOF)
                        notSimbol=1;8*/
                }
//                if(notSimbol==0){
                System.out.println("Erro na formação da String -->" + lexemaStr);
                TableLexema.getInstance().addErrorToList(lexemaStr, index, "Erro na formação da String");
              /*  }
                else{
                System.out.println("Caractere na String não pertence ao alfabeto -->" + lexemaStr);
                TableLexema.getInstance().addErrorToList(lexemaStr, index, "Caractere na String não pertence ao alfabeto");    
                }*/
                break;
            }

        }

        return i;
    }

    public int checkChar(String linha, int index) {
        //tipo = 5 : CARACTERE
        //int count = 0;

        char currentChar;
        char fowardChar;
        int i;
        String lexemaChar = "";
        i = index + 1;

        currentChar = linha.charAt(i);
        fowardChar = linha.charAt(i + 1);
        while (elementText(currentChar)) {
            i++;
            currentChar = linha.charAt(i);
            fowardChar = linha.charAt(i + 1);
        }
        lexemaChar = lexemaChar.concat(String.valueOf(currentChar));

        if (letter.contains(currentChar) || simbol.contains(currentChar) || digit.contains(currentChar)) {
            // se o caractere a seguir é aspas simples, entao correto
            while (elementText(fowardChar)) {
                i++;
                fowardChar = linha.charAt(i);
            }
            if ((fowardChar == (char) 39)) {
                System.out.println("lexama char -->" + currentChar);
                TableLexema.getInstance().addToList(String.valueOf(currentChar), index, 5);
//                    OUTRA FORMA DE ADICIONAR NA LISTA  
//                    IndexTexto aux = TableLexema.getInstance().getIndexTexto(i-1);
//                    Token reserved = new Token(String.valueOf(currentChar), aux.getLinha(), aux.getColuna(), 5);
//                    TableLexema.getInstance().getArray().add(reserved);

                i++;
            } else { //falhou? Consome a maior subcadeia.
                while (fowardChar != (char) 39 && fowardChar != '\u001a') {
                    i++;
                    currentChar = linha.charAt(i);
                    lexemaChar = lexemaChar.concat(String.valueOf(currentChar));
                    fowardChar = linha.charAt(i + 1);
                }
                i++;
                System.out.println("Erro na formação de caracter -->" + lexemaChar);
                TableLexema.getInstance().addErrorToList(lexemaChar, index, "Erro na formação de caracter");
                
            }

        } else {
            IndexTexto colRow = TableLexema.getInstance().getIndexTexto(i);
            System.out.println("Erro na formação de caracter -->" + "Linha-->" + colRow.getLinha() + "Coluna-->" + colRow.getColuna());
            TableLexema.getInstance().addErrorToList(lexemaChar, index, "Erro na formação de caracter");
        }
        return i;
    }

    public int checkOperator(String texto, int index) {
        //tipo = 6 : OPERADOR
        char currentChar;
        char fowardChar;
        fowardChar = ' ';
        int i;
        for (i = index; i < texto.length(); i++) {

            currentChar = texto.charAt(i);
            if (i < texto.length() - 1) {
                fowardChar = texto.charAt(i + 1);
            } else {
                fowardChar = ' ';
            }
            
            if (fowardChar == '=') {
                if (currentChar == '=' || currentChar == '!' || currentChar == '>' || currentChar == '<') {
                    System.out.println("Operador-->" + String.valueOf(currentChar) + String.valueOf(fowardChar));
                    TableLexema.getInstance().addToList(String.valueOf(currentChar).concat(String.valueOf(fowardChar)), i, 6);
                    i++;
                    break;
                }
            } else if ((currentChar == '&' && fowardChar == '&')
                    || (currentChar == '|' && fowardChar == '|')
                    || (currentChar == '+' && fowardChar == '+')
                    || (currentChar == '-' && fowardChar == '-')) {
                System.out.println("Operador-->" + String.valueOf(currentChar) + String.valueOf(fowardChar));
                TableLexema.getInstance().addToList(String.valueOf(currentChar).concat(String.valueOf(fowardChar)), i, 6);

                i++;
                break;
            } else if (currentChar == '+' || currentChar == '-' || currentChar == '<' || currentChar == '>' || currentChar == '=' || currentChar == '.' || currentChar == '*') {
                System.out.println("Operador-->" + String.valueOf(currentChar));
                TableLexema.getInstance().addToList(String.valueOf(currentChar), i, 6);
                break;
            } else {
                System.out.println("Erro na formação do operador-->" + currentChar);
                TableLexema.getInstance().addErrorToList(String.valueOf(currentChar), i, "Erro na formação do operador");
                break;

            }


        }
        return i;
    }

    public int checkComments(String texto, int index) {
        char currentChar;
        char fowardChar;
        boolean flagLineComment = false;
        boolean flagBlockComment = false;
        String lexemaC = "";
        //  fowardChar = ' ';
        int i;
        for (i = index; i < texto.length(); i++) {
            currentChar = texto.charAt(i);
            
            if (i < texto.length() - 1) {
                fowardChar = texto.charAt(i + 1);
            } else {
                fowardChar = '\u001a'; //EOF
            }

            if (flagLineComment) {
                String char2string = String.valueOf(currentChar);

                if (currentChar == '\n' || currentChar == '\u001a') {
                   
                    System.out.println("Comentário linha-->" + lexemaC);
                   
                    break;
                }
                lexemaC = lexemaC.concat(char2string);
            } else if (flagBlockComment) {
                if ((currentChar == (char) 42 && fowardChar == (char) 47)) { //encontrou */ 
                    
                    System.out.println("Bloco de comentário-->" + lexemaC);
                    
                    i++;
                    break;
                } else if (currentChar == '\u001a') { //caso não encontre fica consumindo(concatenando)
                    System.out.println("Erro falta fechar Bloco de comentário */ ");
                    TableLexema.getInstance().addErrorToList(String.valueOf(currentChar), index, "Erro falta fechar Bloco de comentário");
                } else {
                    String char2string = String.valueOf(currentChar);
                    lexemaC = lexemaC.concat(char2string);
                }
            }
            if (flagLineComment == false && flagBlockComment == false) {
                if (fowardChar == (char) 47) { //É comentário de barras duplas
                    flagLineComment = true;
                    i++;
                } else if (fowardChar == (char) 42) {//se comentário for '/*'
                    flagBlockComment = true;
                    i++;
                }

            }
        }
     
        return i;
    }

    public int checkDelimiter(String texto, int index) {
        char currentChar = texto.charAt(index);
        System.out.println("Delimitador -->" + String.valueOf(currentChar));
        TableLexema.getInstance().addToList(String.valueOf(currentChar), index, 7);
        return index++;
    }

    public int checkNumbers(String texto, int index) {
        String number = new String();
        char currentChar;
        int i = index;
        boolean stateErrorNumber = false;
        boolean stateOneDote = false;
        boolean stateDotNumber = false;
        currentChar = texto.charAt(index);

        if ((digit.contains(currentChar) || currentChar == '-')) {
            number = number.concat(String.valueOf(currentChar)); //concatena para formar o lexema
            i++;
        }

        currentChar = texto.charAt(i);
        while (getDigit().contains(currentChar)) { //enquanto digito vai consumindo a cadeia e concatenando o lexema.
            number = number.concat(String.valueOf(currentChar));
            i++;
            currentChar = texto.charAt(i);
        }
        if (currentChar == '.') { //se for ponto indica que já tem um ponto
            number = number.concat(String.valueOf(currentChar));
            i++;
            currentChar = texto.charAt(i);
            stateOneDote = true;
        } else if (currentChar == ' '   //se for espaço, elemento de formatação de texto, EOF, operador salvo ponto e delimitado. Então o número está formado 
                || elementText(currentChar)
                || currentChar == Triagem.EOF
                || getOperator().contains(currentChar)
                || getDelimeters().contains(currentChar)) {
            i--;
            //número bem formado. Adiciona a tabela de símbolos
            System.out.println("Número --> " + number);
            TableLexema.getInstance().addToList(String.valueOf(number), i , 8);
            return i;
        } else { //caso contrário o trata-se de um erro de formação de número.
            number = number.concat(String.valueOf(currentChar));
            i++;
            currentChar = texto.charAt(i);
            stateErrorNumber = true;
        }

        if (stateOneDote) { //Estado que indica que recebeu o primeiro ponto. Por exemplo, 2.
            if (getDigit().contains(currentChar)) { //se recebeu um dígito  passa para outro estado indicando que até o momento existe um número bem formado
                stateDotNumber = true;
                number = number.concat(String.valueOf(currentChar));
                i++;
                currentChar = texto.charAt(i);
            } else if (currentChar == Triagem.EOF) { //chegou ao final do arquivo. Erro, mas na consome nenhuma caractere.
                stateErrorNumber = true;
            } else {
                stateErrorNumber = true;
                number = number.concat(String.valueOf(currentChar));
                i++;
                currentChar = texto.charAt(i);
            }
        }
        if (stateDotNumber) { // estado correspondente ao dígitos, ponto e dígito. Por exemplo, 27.456
            while (getDigit().contains(currentChar)) { //enquanto digito vai consumindo a cadeia e concatenando o lexema.
                number = number.concat(String.valueOf(currentChar));
                i++;
                currentChar = texto.charAt(i);
            }
            if (currentChar == '.') { //caso receba um novo ponto. Erro.
                number = number.concat(String.valueOf(currentChar));
                i++;
                currentChar = texto.charAt(i);
                stateErrorNumber = true;
            } else if (currentChar == ' ' //se for espaço, elemento de formatação de texto, EOF, operador salvo ponto e delimitado. Então o número está formado 
                    || elementText(currentChar)
                    || currentChar == Triagem.EOF
                    || getOperator().contains(currentChar)
                    || getDelimeters().contains(currentChar)) {
                i--;
                //número bem formado. Adiciona a tabela de símbolos
                System.out.println("Número --> " + number);
                TableLexema.getInstance().addToList(String.valueOf(number), i, 8);
                return i;
            } else { //caso contrário erro
                stateErrorNumber = true;
                number = number.concat(String.valueOf(currentChar));
                i++;
                currentChar = texto.charAt(i);
            }
        }
        if (stateErrorNumber) { //estado de erro
            while (currentChar != ' ' && !elementText(currentChar) && currentChar != Triagem.EOF) { //consome tudo até encontrar espaço, elemento de formantação de texto ou EOF.
                number = number.concat(String.valueOf(currentChar));
                i++;
                currentChar = texto.charAt(i);
            }
            //adiciona na tabela de erro.
            i--;
            System.out.println("Erro Número --> " + number);
            TableLexema.getInstance().addErrorToList(String.valueOf(number), i, "Erro na formação do número");
            return i;
        }

        return i;

    }

    public boolean elementText(char character) {
        boolean flag = false;
        if (character == '\n' || character == '\t') {
            flag = true;
        } else {
            flag = false;
        }
        return flag;
    }

    public Collection getLetter() {
        return letter;
    }

    public Collection getDigit() {
        return digit;
    }

    public Collection getSimbol() {
        return simbol;
    }

    public Collection getIdentifier() {
        return identifier;
    }

    public Collection getOperator() {
        return operator;
    }

    public Collection getDelimeters() {
        return delimeters;
    }

    public Collection getReservedWord() {
        return reservedWord;
    }

    private void countRowColumn(char currentChar) {
        if (currentChar == '\n') {
            tabelaSimbolo.incrementRow();
        } else {
            tabelaSimbolo.incrementColumn();
        }
    }
    
    public String getLetraUP() {
        return letraUP;
    }

    public void setLetraUP(String letraUP) {
        this.letraUP = letraUP;
    }

    public Collection getNotAlphabet() {
        return notAlphabet;
    }

    public void setNotAlphabet(Collection notAlphabet) {
        this.notAlphabet = notAlphabet;
    }

    public TableLexema getTabelaSimbolo() {
        return tabelaSimbolo;
    }

    public void setTabelaSimbolo(TableLexema tabelaSimbolo) {
        this.tabelaSimbolo = tabelaSimbolo;
    }

}

